"use client"; import { useState, useEffect, useMemo, useCallback } from "react"; import { useDropzone } from "react-dropzone"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { Skeleton } from "@/components/ui/skeleton"; import { InfoIcon, Upload } from "lucide-react"; import { SwpTable } from "@/lib/swp/table/swp-table"; import { SwpTableToolbar } from "@/lib/swp/table/swp-table-toolbar"; import { fetchVendorDocuments, fetchVendorProjects, fetchVendorSwpStats, getVendorSessionInfo, } from "@/lib/swp/vendor-actions"; import type { DocumentListItem } from "@/lib/swp/document-service"; import { toast } from "sonner"; interface VendorDocumentPageProps { searchParams: { [key: string]: string | string[] | undefined }; } export default function VendorDocumentPage({ searchParams }: VendorDocumentPageProps) { // URL에서 프로젝트 번호만 사용 (나머지는 클라이언트 필터링) const initialProjNo = (searchParams.projNo as string) || ""; // 상태 관리 const [documents, setDocuments] = useState([]); const [projNo, setProjNo] = useState(initialProjNo); const [projects, setProjects] = useState>([]); const [stats, setStats] = useState({ total_documents: 0, total_revisions: 0, total_files: 0, uploaded_files: 0, last_sync: null as Date | null, }); const [vendorInfo, setVendorInfo] = useState<{ vendorId: number; vendorCode: string; vendorName: string; companyId: number; } | null>(null); const [isLoading, setIsLoading] = useState(true); const [isRefreshing, setIsRefreshing] = useState(false); const [error, setError] = useState(null); // 클라이언트 필터 const [searchFilters, setSearchFilters] = useState({ docNo: (searchParams.docNo as string) || "", docTitle: (searchParams.docTitle as string) || "", pkgNo: (searchParams.pkgNo as string) || "", stage: (searchParams.stage as string) || "", }); // Dropzone 설정 const [droppedFiles, setDroppedFiles] = useState([]); // 파일 드롭 핸들러 const onDrop = useCallback((acceptedFiles: File[]) => { if (acceptedFiles.length > 0) { setDroppedFiles(acceptedFiles); } }, []); const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop, noClick: true, // 클릭으로 파일 선택 비활성화 (버튼을 통해서만 선택) noKeyboard: true, }); const loadInitialData = useCallback(async () => { try { setIsLoading(true); setError(null); // 병렬로 데이터 로드 const [vendorInfoData, projectsData] = await Promise.all([ getVendorSessionInfo(), fetchVendorProjects(), ]); setVendorInfo(vendorInfoData); setProjects(projectsData); // 초기 프로젝트가 있으면 문서 로드 if (initialProjNo) { const [documentsData, statsData] = await Promise.all([ fetchVendorDocuments(initialProjNo), fetchVendorSwpStats(initialProjNo), ]); setDocuments(documentsData); setStats(statsData); } } catch (err) { console.error("초기 데이터 로드 실패:", err); setError(err instanceof Error ? err.message : "데이터 로드 실패"); toast.error("데이터 로드 실패"); } finally { setIsLoading(false); } }, [initialProjNo]); const loadDocuments = useCallback(async () => { if (!projNo) return; try { setIsRefreshing(true); setError(null); const [documentsData, statsData] = await Promise.all([ fetchVendorDocuments(projNo), fetchVendorSwpStats(projNo), ]); setDocuments(documentsData); setStats(statsData); toast.success("문서 목록을 갱신했습니다"); } catch (err) { console.error("문서 로드 실패:", err); setError(err instanceof Error ? err.message : "문서 로드 실패"); toast.error("문서 로드 실패"); } finally { setIsRefreshing(false); } }, [projNo]); // 초기 데이터 로드 useEffect(() => { loadInitialData(); }, [loadInitialData]); // 프로젝트 변경 시 문서 재로드 useEffect(() => { if (!isLoading && projNo) { loadDocuments(); } }, [projNo, isLoading, loadDocuments]); const handleProjectChange = (newProjNo: string) => { setProjNo(newProjNo); }; const handleFiltersChange = (filters: typeof searchFilters) => { setSearchFilters(filters); }; const handleRefresh = () => { loadDocuments(); }; // 클라이언트 사이드 필터링 const filteredDocuments = useMemo(() => { return documents.filter((doc) => { if (searchFilters.docNo && !doc.DOC_NO.toLowerCase().includes(searchFilters.docNo.toLowerCase())) { return false; } if (searchFilters.docTitle && !doc.DOC_TITLE.toLowerCase().includes(searchFilters.docTitle.toLowerCase())) { return false; } if (searchFilters.pkgNo && !doc.PKG_NO?.toLowerCase().includes(searchFilters.pkgNo.toLowerCase())) { return false; } if (searchFilters.stage && doc.STAGE !== searchFilters.stage) { return false; } return true; }); }, [documents, searchFilters]); if (isLoading) { return ( ); } return (
{/* 드래그 오버레이 */} {isDragActive && (

파일을 여기에 드롭하세요

여러 파일을 한 번에 업로드할 수 있습니다

)}
{/* 에러 메시지 */} {error && ( {error} )} {/* 통계 카드 */}
할당된 문서 {stats.total_documents.toLocaleString()} 총 리비전 {stats.total_revisions.toLocaleString()} 총 파일 {stats.total_files.toLocaleString()} 업로드한 파일 {stats.uploaded_files.toLocaleString()}
{/* 안내 메시지 */} {documents.length === 0 && !projNo && ( 프로젝트를 선택하여 할당된 문서를 확인하세요. )} {/* 메인 테이블 */} setDroppedFiles([])} documents={filteredDocuments} userId={String(vendorInfo?.vendorId || "")} />
); }